package com.qimu.saiyou.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qimu.saiyou.common.ErrorCode;
import com.qimu.saiyou.exception.BusinessException;
import com.qimu.saiyou.exception.ThrowUtils;
import com.qimu.saiyou.mapper.JoinCompeteMapper;
import com.qimu.saiyou.model.dto.joincompete.JoinCompeteRequest;
import com.qimu.saiyou.model.entity.Competition;
import com.qimu.saiyou.model.entity.JoinCompete;
import com.qimu.saiyou.model.entity.Team;
import com.qimu.saiyou.model.entity.User;
import com.qimu.saiyou.service.CompetitionService;
import com.qimu.saiyou.service.JoinCompeteService;
import com.qimu.saiyou.service.TeamService;
import com.qimu.saiyou.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author: FeiXiang
 * @description 针对表【join_compete(参加竞赛)】的数据库操作Service实现
 * @createDate 2024-02-26 15:49:26
 */
@Service
@Slf4j
public class JoinCompeteServiceImpl extends ServiceImpl<JoinCompeteMapper, JoinCompete>
        implements JoinCompeteService {
    @Resource
    private RedisTemplate<String, Page<Competition>> redisTemplate;
    @Resource
    private CompetitionService competitionService;
    @Resource
    private UserService userService;
    @Resource
    private TeamService teamService;

    /**
     * 加入竞争
     *
     * @param joinCompeteRequest 加入竞争添加请求
     * @param loginUser          登录用户
     * @return boolean
     */
    @Override
    public boolean doJoinCompete(JoinCompeteRequest joinCompeteRequest, User loginUser) {
        Long teamId = joinCompeteRequest.getTeamId();
        Long competitionId = joinCompeteRequest.getCompetitionId();
        Competition competition = competitionService.getById(competitionId);
        ThrowUtils.throwIf(competition == null, ErrorCode.NOT_FOUND_ERROR, "竞赛不存在");
        String signUpDeadline = competition.getSignUpDeadline();
        // 获取当前时间
        LocalDateTime currentTime = LocalDateTime.now();
        // 定义时间格式化器
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        // 将报名截止时间字符串按照指定格式解析为LocalDateTime对象
        LocalDateTime deadline = LocalDateTime.parse(signUpDeadline, formatter);
        // 判断当前时间是否在报名截止时间之后
        ThrowUtils.throwIf(currentTime.isAfter(deadline), ErrorCode.NOT_FOUND_ERROR, "报名已截止");
        if ("end".equals(competition.getCompetitionStatus())) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "竞赛已结束");
        }
        if ("cancel".equals(competition.getCompetitionStatus())) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "竞赛已取消");
        }
        if ("running".equals(competition.getCompetitionStatus())) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "竞赛已开始");
        }
        Team team = teamService.getById(teamId);
        ThrowUtils.throwIf(team == null, ErrorCode.NOT_FOUND_ERROR, "队伍不存在");
        if (!userService.isAdmin(loginUser) && !team.getTeamLeaderId().equals(loginUser.getId()) && !team.getTeamCaptainId().equals(loginUser.getId())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "权限不足！请通知队长或导师报名竞赛！");
        }
        if (JSONUtil.toList(team.getUsersId(), Long.class).size() > competition.getMaxNum()) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "当前队伍人数已超限！赛事要求队伍最多" + competition.getTeamMaxNum() + "人参赛");
        }
        if (competition.getCompetitiveMode() == 0 && JSONUtil.toList(team.getUsersId(), Long.class).size() > 1) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "当前赛事为单人模式，队伍只能一人参赛！");

        }
        ThrowUtils.throwIf(!team.getUsersId().contains(String.valueOf(loginUser.getId())), ErrorCode.OPERATION_ERROR, "该用户不在队伍");
        LambdaQueryWrapper<JoinCompete> joinCompeteLambdaQueryWrapper = new LambdaQueryWrapper<>();
        joinCompeteLambdaQueryWrapper.eq(JoinCompete::getCompetitionId, competitionId);
        joinCompeteLambdaQueryWrapper.eq(JoinCompete::getTeamId, teamId);
        JoinCompete oldJoinCompete = this.getOne(joinCompeteLambdaQueryWrapper);
        ThrowUtils.throwIf(oldJoinCompete != null, ErrorCode.NOT_FOUND_ERROR, "禁止重复参加");
        JoinCompete joinCompete = new JoinCompete();
        joinCompete.setTeamId(team.getId());
        joinCompete.setCompetitionId(competition.getId());
        team.setCurrentCompetitionId(competitionId);
        teamService.updateById(team);
        // 清除缓存
        redisTemplate.delete("listMyJoinCompeteByPage:" + loginUser.getUserAccount());
        return this.save(joinCompete);
    }

    @Override
    public boolean doQuitCompete(JoinCompeteRequest joinCompeteRequest, User loginUser) {
        Long teamId = joinCompeteRequest.getTeamId();
        Long competitionId = joinCompeteRequest.getCompetitionId();
        Competition competition = competitionService.getById(competitionId);
        ThrowUtils.throwIf(competition == null, ErrorCode.NOT_FOUND_ERROR, "竞赛不存在");
        Team team = teamService.getById(teamId);
        ThrowUtils.throwIf(team == null, ErrorCode.NOT_FOUND_ERROR, "队伍不存在");
        if (!userService.isAdmin(loginUser) && !team.getTeamLeaderId().equals(loginUser.getId()) && !team.getTeamCaptainId().equals(loginUser.getId())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "权限不足！请通知队长或导师退出竞赛！");
        }
        ThrowUtils.throwIf(!team.getUsersId().contains(String.valueOf(loginUser.getId())), ErrorCode.OPERATION_ERROR, "该用户不在队伍");
        LambdaQueryWrapper<JoinCompete> joinCompeteLambdaQueryWrapper = new LambdaQueryWrapper<>();
        joinCompeteLambdaQueryWrapper.eq(JoinCompete::getCompetitionId, competitionId);
        joinCompeteLambdaQueryWrapper.eq(JoinCompete::getTeamId, teamId);
        team.setCurrentCompetitionId(0L);
        teamService.updateById(team);
        // 清除缓存
        redisTemplate.delete("listMyJoinCompeteByPage:" + loginUser.getUserAccount());
        return this.remove(joinCompeteLambdaQueryWrapper);
    }

    @Override
    public Page<Competition> listMyJoinCompeteByPage(User loginUser) {
        Page<Competition> redisCompetitionList = redisTemplate.opsForValue().get("listMyJoinCompeteByPage:" + loginUser.getUserAccount());
        if (ObjectUtils.isNotEmpty(redisCompetitionList) && CollUtil.isNotEmpty(redisCompetitionList.getRecords())) {
            log.info("listMyJoinCompeteByPage by redis");
            return redisCompetitionList;
        }
        Page<Competition> competitionPage = new Page<>();
        LambdaQueryWrapper<Team> teamLambdaQueryWrapper = new LambdaQueryWrapper<>();
        teamLambdaQueryWrapper.like(Team::getUsersId, loginUser.getId());
        // 获取加入的队伍id
        List<Long> joinTeamUserIdList = teamService.list(teamLambdaQueryWrapper).stream().map(Team::getId).collect(Collectors.toList());
        if (CollUtil.isEmpty(joinTeamUserIdList)) {
            return new Page<>();
        }
        // 获取队伍参加竞赛信息
        LambdaQueryWrapper<JoinCompete> joinCompeteLambdaQueryWrapper = new LambdaQueryWrapper<>();
        joinCompeteLambdaQueryWrapper.in(JoinCompete::getTeamId, joinTeamUserIdList);
        List<Long> joinCompeteIdList = this.list(joinCompeteLambdaQueryWrapper).stream().map(JoinCompete::getCompetitionId).collect(Collectors.toList());
        if (CollUtil.isEmpty(joinCompeteIdList)) {
            return new Page<>();
        }
        // 获取竞赛信息
        List<Competition> competitionList = competitionService.listByIds(joinCompeteIdList);
        competitionPage.setRecords(competitionList);
        redisTemplate.opsForValue().set("listMyJoinCompeteByPage:" +loginUser.getUserAccount(), competitionPage, 3, TimeUnit.MINUTES);
        return competitionPage;
    }

}




